/**********************************************************************\

 Copyright 2012 Diggory Blake

 This file is part of klip-engine

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

\**********************************************************************/
#pragma once

struct kData;
struct kDataView;

class kStream;

#include "kbase.h"
#include "kcommon.h"
#include "kestreamops.h"
#include "keseekmode.h"
#include "kerror.h"
#include "kstructdata.h"

class kStream : public kBase {
private:
	keStreamOps _streamOps;

protected:
	size_t _size;
	size_t _available;
	size_t _readOffset;
	size_t _writeOffset;

	kStream(keStreamOps ops) {
		_streamOps = ops;
		_size = 0;
		_available = 0;
		_readOffset = 0;
		_writeOffset = 0;
	}

public:
	virtual size_t write(const kDataView& data) = 0;
	virtual size_t read(const kData& data) = 0;
	virtual void seek(keSeekMode mode, size_t offset) = 0;
	virtual void flush() = 0;
	inline size_t tell(keSeekMode mode) {
		switch (mode) {
		case keSeek_Read:
			return _readOffset;
		case keSeek_Write:
			return _writeOffset;
		default:
			throw kError(_T("Invalid seek mode!"));
		}
	}
	inline void skip(keSeekMode mode, size_t offset) {
		seek(mode, tell(mode) + offset);
	}

	inline keStreamOps getOps() {
		return _streamOps;
	}
	inline bool canRead() {
		return _streamOps & keStream_Read ? true : false;
	}
	inline bool canWrite() {
		return _streamOps & keStream_Write ? true : false;
	}
	inline bool canSeek() {
		return _streamOps & keStream_Seek ? true : false;
	}
	inline bool canMeasure() {
		return _streamOps & keStream_Measure ? true : false;
	}
	inline bool canExpand() {
		return _streamOps & keStream_Expand ? true : false;
	}
	inline size_t getSize() {
		if (!canMeasure()) {
			throw kError(_T("Stream does not support measuring its size!"));
		}
		return _size;
	}
	inline size_t getAvailable() {
		return _available;
	}

	bool match(const kDataView& data);
	template<typename T> inline size_t readT(T& data) {
		return read(kData((uint8_t*)&data, sizeof(T)));
	}
	template<typename T> inline size_t writeT(const T& data) {
		return write(kDataView((const uint8_t*)&data, sizeof(T)));
	}
	template<typename T> inline bool matchT(const T& data) {
		return match(kDataView((const uint8_t*)&data, sizeof(T)));
	}
	template<typename T> inline void skipT(keSeekMode mode) {
		skip(mode, sizeof(T));
	}
};
